package com.wj.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.wj.config.MinioConfig;
import com.wj.exception.MinioException;
import com.wj.exception.enums.MinioExpCode;
import com.wj.service.ITResourceService;
import com.wj.service.ImageAuditService;
import com.wj.service.MinioService;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InvalidResponseException;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j  // 启用Lombok日志注解
public class MinioServiceImpl implements MinioService {
    private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList(
            ".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".txt"
    );
    private static final long MAX_FILE_SIZE = 20 * 1024 * 1024;
    @Autowired
    MinioConfig minioConfig;
    @Autowired
    MinioClient minioClient;
    @Autowired
    ITResourceService iTResourceService;
    @Autowired
    ImageAuditService imageAuditService;
    // 在 MinioServiceImpl 中初始化时设置策略
    @PostConstruct
    public void init() {
        try {
            // 检查并设置存储桶公共读取策略
            setPublicReadPolicy(minioConfig.getBucket());
        } catch (Exception e) {
            System.err.println("初始化存储桶策略失败: " + e.getMessage());
        }
    }

    @Override
    public String uploadFile(MultipartFile file) {
        // 1. 空文件校验
        if (file.isEmpty()) {
            throw new MinioException(MinioExpCode.UPLOAD_FILE_EMPTY);
        }

        // 2. 文件大小校验（优化错误提示）
        if (file.getSize() > MAX_FILE_SIZE) {
            throw new MinioException(MinioExpCode.UPLOAD_FILE_TOO_LARGE);
        }

        // 3. 文件名与扩展名处理（修复NPE和格式问题）
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || !originalFilename.contains(".")) {
            throw new MinioException(MinioExpCode.UPLOAD_FILE_INVALID_NAME);
        }

        // 提取扩展名（统一转为小写，增强兼容性）
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
        // 校验允许的扩展名
        if (!ALLOWED_EXTENSIONS.contains(fileExtension)) {
            throw new MinioException(MinioExpCode.UPLOAD_FILE_INVALID_TYPE
                    );
        }

        try {
            // 关键修改：区分文件类型处理编码
            InputStream inputStream;
            String contentType;
            // 文本文件（如txt、csv）强制UTF-8编码
            if (Set.of(".txt", ".csv", ".json", ".xml").contains(fileExtension.toLowerCase())) {
                // 按UTF-8读取原文件内容，再以UTF-8写入流
                String content = new String(file.getBytes(), StandardCharsets.UTF_8);

                inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
                contentType = "text/plain; charset=utf-8"; // 明确标注编码
            } else {
                // 二进制文件（如docx、pdf、图片）直接用原始字节流
                inputStream = file.getInputStream();
                contentType = file.getContentType() != null ? file.getContentType() : "application/octet-stream";
            }
            // 4. 存储桶操作（添加权限配置）
            String bucketName = minioConfig.getBucket();
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                // 设置公共读权限（根据业务需求调整）
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                        .bucket(bucketName)
                        .config("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}")
                        .build());
            }

            // 5. 生成唯一文件名（优化路径结构）
            String dateDir = DateUtil.format(new Date(), "yyyyMMdd"); // 日期目录（如20241010）
            String uuid = UUID.randomUUID().toString().replace("-", "");
            String objectName = String.format("%s/%s%s", dateDir, uuid, fileExtension);

            // 6. 上传文件（优化ContentType和异常捕获）
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, file.getSize(), -1)  // 使用处理后的 inputStream
                    .contentType(contentType)  // 使用处理后的 contentType
                    .build());

            log.info("文件上传成功：{}，大小：{}KB", objectName, file.getSize() / 1024);
            return objectName;

        } catch (IOException e) {
            log.error("文件输入流处理失败", e);
            throw new MinioException(MinioExpCode.UPLOAD_FILE_STREAM_ERROR);
        } catch (InvalidResponseException | ErrorResponseException e) {
            log.error("MinIO服务响应错误", e);
            throw new MinioException(MinioExpCode.UPLOAD_FILE_SERVER_ERROR);
        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new MinioException(MinioExpCode.UPLOAD_FILE_FAILED);
        }
    }


    @Override
    public Integer deleteFiles(String fileNames) {
        List<String> fileNameList = (List<String>) JSONUtil.parse(fileNames);

        try{
            // 构建删除对象列表
            List<DeleteObject> deleteObjects = fileNameList.stream()
                    .map(DeleteObject::new)
                    .collect(Collectors.toList());

// 批量删除
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(
                    RemoveObjectsArgs.builder()
                            .bucket(minioConfig.getBucket())
                            .objects(deleteObjects)
                            .build());

// 处理删除结果（可选）
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                System.out.println("删除失败: " + error.objectName() + ", 错误: " + error.message());
            }

            return 1;
        }catch (Exception e){
            throw new MinioException(MinioExpCode.DELETE_FILE_FAILED);
        }
    }

    // 修改 getFileUrl 方法以支持永久访问
    @Override
    public String getFileUrl(String fileName) {
        try {
            // 1. 构造基础URL
            String baseUrl = minioConfig.getEndpoint() + "/" + minioConfig.getBucket() + "/" + fileName;

            // 2. 处理文本文件的编码参数（关键：让浏览器用UTF-8解码）
//            if (isTextFile(fileName)) {
//                // 检查URL是否已有参数，决定用?还是&拼接
//                String separator = baseUrl.contains("?") ? "&" : "?";
//                // 添加编码参数（部分浏览器需要显式指定charset）
//                baseUrl += separator + "response-content-type=text/plain;charset=utf-8";
//            }

            System.out.println("文件访问URL: " + baseUrl);
            return baseUrl;
        } catch (Exception e) {
            e.printStackTrace();
            throw new MinioException(MinioExpCode.GET_PRESIGNED_URL_FAILED);
        }
    }

    // 可以删除未使用的 getFileExtension 方法，或修改 isTextFile 方法使用它：
    private boolean isTextFile(String fileName) {
        if (fileName == null) return false;

        String fileExtension = getFileExtension(fileName).toLowerCase();
        String[] textExtensions = {".txt", ".csv", ".json", ".xml"};

        for (String ext : textExtensions) {
            if (ext.equals(fileExtension)) {
                return true;
            }
        }
        return false;
    }

    // 保留并使用 getFileExtension 方法
    private String getFileExtension(String fileName) {
        if (fileName == null || !fileName.contains(".")) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf("."));
    }

    // 在 MinioServiceImpl 中添加策略配置方法
    public void setPublicReadPolicy(String bucketName) {
        try {
            // 创建允许公共读取的策略
            String policy = "{\n" +
                    "  \"Version\": \"2012-10-17\",\n" +
                    "  \"Statement\": [\n" +
                    "    {\n" +
                    "      \"Effect\": \"Allow\",\n" +
                    "      \"Principal\": {\n" +
                    "        \"AWS\": [\"*\"]\n" +
                    "      },\n" +
                    "      \"Action\": [\"s3:GetObject\"],\n" +
                    "      \"Resource\": [\"arn:aws:s3:::" + bucketName + "/*\"]\n" +
                    "    }\n" +
                    "  ]\n" +
                    "}";

            minioClient.setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                            .bucket(bucketName)
                            .config(policy)
                            .build()
            );
        } catch (Exception e) {
            throw new MinioException(MinioExpCode.SET_BUCKET_POLICY_FAILED);
        }
    }

    @Override
    public Integer deleteFile(String fileName) {
        try{
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(minioConfig.getBucket())
                            .object(fileName)
                            .build()
            );
            return 1;
        }catch (Exception e){
            throw new MinioException(MinioExpCode.DELETE_FILE_FAILED);
        }
    }

    @Override
    public List<String> getFileUrls(String fileNames) {
        try{
            System.out.println("fileNames = " + fileNames);
            List<String> fileNameList = (List<String>) JSONUtil.parse(fileNames);
            System.out.println("fileNameList = " + fileNameList);
            List<String> urls = new ArrayList<>();
            for (String fileName : fileNameList) {
                // 判断对象是否存在
                minioClient.statObject(
                        StatObjectArgs.builder()
                                .bucket(minioConfig.getBucket())
                                .object(fileName)
                                .build()
                );
                // 获取文件访问URL
                GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(minioConfig.getBucket())
                        .object(fileName)
//                        .expiry(minioConfig.getExpireMinutes())
                        .build();
                urls.add(minioClient.getPresignedObjectUrl(args));
            }
            return urls;
        }catch (Exception e){
            throw new MinioException(MinioExpCode.GET_PRESIGNED_URL_FAILED);
        }

    }
}
